//
// Created by SongpingWang on 2024/10/9.
//
#ifndef ZDATAFRAME_COLUMNVIEW_H
#define ZDATAFRAME_COLUMNVIEW_H

#include <vector>
#include <string>
#include <regex>
#include <functional>
#include <vector>
#include <numeric>
#include <stdexcept>
#include <type_traits>
#include <algorithm>
#include <limits>
#include <map>

#ifndef MAYBE_USE
#define MAYBE_USE  [[maybe_unused]] [[nodiscard]]
#define VOID_USE   [[maybe_unused]]
#endif


#ifdef _WIN32
#ifndef ZDataFrame_DLL
#define ZDFrame_API __declspec(dllexport)
#else
#define ZDFrame_API __declspec(dllimport)
#endif
#else
#define ZDFrame_API
#endif

namespace NDFrame {
    template<typename StringType>
    class ZDFrame_API ZDataBase;

    template<typename StringType>
    struct DataItem;

    template<typename StringType, typename T>
    class ZDFrame_API ColView {
        using ItemType = DataItem<StringType>;

        using FieldInt = ColView<StringType, int>;
        using FieldStr = ColView<StringType, StringType>;

        using BaseType = ZDataBase<StringType>;
        using CharType = typename StringType::value_type;
        using VecItemPtr = std::vector<DataItem<StringType> *>;
        using RegexType = typename std::conditional<std::is_same<StringType, std::string>::value, std::regex, std::wregex>::type;
        using VecRegex = std::vector<RegexType>;

        MAYBE_USE inline static bool matches_regex(const StringType &text, const RegexType &regex_) {
            return std::regex_search(text, regex_);
        }

    private:
        const BaseType &m_df;
        T ItemType::*member;

    public:
        ColView(const BaseType &dataFrame, T ItemType::*member) : m_df(dataFrame), member(member) {}

        [[nodiscard]] std::vector<std::reference_wrapper<T>> values() const;

        [[nodiscard]] size_t size() const { return m_df.size(); }

        VOID_USE void sub(int num);

        VOID_USE void add(int num, int max_num);

        MAYBE_USE float mean_col() const;

        MAYBE_USE size_t max_col() const;

        MAYBE_USE size_t min_col() const;

        MAYBE_USE auto len() const -> std::vector<size_t>;

        VOID_USE void sort(bool descending = false);

        VOID_USE void replace(CharType oldChar, CharType newChar);

        VOID_USE void replace(CharType oldChar0, CharType newChar0, CharType oldChar1, CharType newChar1);

        VOID_USE void multi_replace(const std::map<StringType, StringType> &replacements) {
            multi_replace_df_text_impl(replacements);
        }

        VOID_USE void multi_replace(const std::unordered_map<StringType, StringType> &replacements) {
            multi_replace_df_text_impl(replacements);
        }

        /*ZDataFrame<StringType> isin(const std::vector<StringType>& field) const;*/

        /*
        * 仿照python的join函数
        * 默认为空字符串
        * join_replace对ZDataFrame的text合并后的字符串中的字符进行替换
        * const StringType& Char_为分隔符
        */
        MAYBE_USE StringType join(const StringType &Char_ = StringType()) const;

        MAYBE_USE StringType join_replace(CharType oldChar,
                                          const CharType *newChar) const;

        MAYBE_USE StringType join_replace(CharType oldChar,
                                          CharType newChar,
                                          const StringType &Char_ = StringType()) const;

        MAYBE_USE StringType join_replace2(CharType oldChar0,
                                           CharType newChar0,
                                           CharType oldChar1,
                                           CharType newChar1,
                                           const StringType &Char_ = StringType()) const;

        /* first filter*/
        MAYBE_USE bool loc_regex(VecItemPtr &out_ptr, const RegexType &regex_) const {
            return ptr_reg_impl(out_ptr, VecRegex{regex_});
        }

        MAYBE_USE bool loc_regex(VecItemPtr &out_ptr, const VecRegex &regex_) const {
            return ptr_reg_impl(out_ptr, regex_);
        }

        MAYBE_USE bool loc_regex_grate_strlen(VecItemPtr &out_ptr, const RegexType &regex_, int len) const {
            return ptr_reg_common4(out_ptr, VecRegex{regex_}, len, std::greater<>());
        }

        MAYBE_USE bool loc_regex_equal_strlen(VecItemPtr &out_ptr, const RegexType &regex_, int len) const {
            return ptr_reg_common4(out_ptr, VecRegex{regex_}, len, std::equal_to<>());
        }

        MAYBE_USE bool loc_regex_less_strlen(VecItemPtr &out_ptr, const RegexType &regex_, int len) const {
            return ptr_reg_common4(out_ptr, VecRegex{regex_}, len, std::less<>());
        }

        MAYBE_USE bool loc_regex_grate_strlen(VecItemPtr &out_ptr, const VecRegex &regex_, int len) const {
            return ptr_reg_common4(out_ptr, regex_, len, std::greater<>());
        }

        MAYBE_USE bool loc_regex_equal_strlen(VecItemPtr &out_ptr, const VecRegex &regex_, int len) const {
            return ptr_reg_common4(out_ptr, regex_, len, std::equal_to<>());
        }

        MAYBE_USE bool loc_regex_less_strlen(VecItemPtr &out_ptr, const VecRegex &regex_, int len) const {
            return ptr_reg_common4(out_ptr, regex_, len, std::less<>());
        }

        MAYBE_USE bool
        loc_regex_xyxy(VecItemPtr &out_ptr, const RegexType &regex_, int x0, int y0, int x1, int y1) const {
            return ptr_reg_common3(out_ptr, VecRegex{regex_},
                                   [x0, y0, x1, y1](const ItemType &item) {
                                       return item.x0 > x0 && item.x1 < x1 && item.y0 > y0 && item.y1 < y1;
                                   });
        }

        MAYBE_USE bool loc_regex_x0x1(VecItemPtr &out_ptr, const RegexType &regex_, int x0, int x1) const {
            return ptr_reg_common3(out_ptr, VecRegex{regex_},
                                   [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE bool loc_regex_y0y1(VecItemPtr &out_ptr, const RegexType &regex_, int y0, int y1) const {
            return ptr_reg_common3(out_ptr, VecRegex{regex_},
                                   [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE bool loc_regex_x0y0(VecItemPtr &out_ptr, const RegexType &regex_, int x0, int y0) const {
            return ptr_reg_common3(out_ptr, VecRegex{regex_},
                                   [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE bool loc_regex_x1y1(VecItemPtr &out_ptr, const RegexType &regex_, int x1, int y1) const {
            return ptr_reg_common3(out_ptr, VecRegex{regex_},
                                   [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }

        MAYBE_USE bool loc_regex_x0x1(VecItemPtr &out_ptr, const VecRegex &regex_, int x0, int x1) const {
            return ptr_reg_common3(out_ptr, regex_,
                                   [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE bool loc_regex_y0y1(VecItemPtr &out_ptr, const VecRegex &regex_, int y0, int y1) const {
            return ptr_reg_common3(out_ptr, regex_,
                                   [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE bool loc_regex_x0y0(VecItemPtr &out_ptr, const VecRegex &regex_, int x0, int y0) const {
            return ptr_reg_common3(out_ptr, regex_,
                                   [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE bool loc_regex_x1y1(VecItemPtr &out_ptr, const VecRegex &regex_, int x1, int y1) const {
            return ptr_reg_common3(out_ptr, regex_,
                                   [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }


        /* second filter*/
        MAYBE_USE bool loc2_regex(VecItemPtr &inout_ptr, const RegexType &regex_) const {
            return ptr2_reg_impl(inout_ptr, VecRegex{regex_});
        }

        MAYBE_USE bool loc2_regex(VecItemPtr &inout_ptr, const VecRegex &regex_) const {
            return ptr2_reg_impl(inout_ptr, regex_);
        }


        MAYBE_USE bool loc2_regex_grate_strlen(VecItemPtr &inout_ptr, const RegexType &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, VecRegex{regex_}, len, std::greater<>());
        }

        MAYBE_USE bool loc2_regex_equal_strlen(VecItemPtr &inout_ptr, const RegexType &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, VecRegex{regex_}, len, std::equal_to<>());
        }

        MAYBE_USE bool loc2_regex_less_strlen(VecItemPtr &inout_ptr, const RegexType &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, VecRegex{regex_}, len, std::less<>());
        }

        MAYBE_USE bool loc2_regex_grate_strlen(VecItemPtr &inout_ptr, const VecRegex &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, regex_, len, std::greater<>());
        }

        MAYBE_USE bool loc2_regex_equal_strlen(VecItemPtr &inout_ptr, const VecRegex &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, regex_, len, std::equal_to<>());
        }

        MAYBE_USE bool loc2_regex_less_strlen(VecItemPtr &inout_ptr, const VecRegex &regex_, int len) const {
            return ptr2_reg_common4(inout_ptr, regex_, len, std::less<>());
        }


        MAYBE_USE bool
        loc2_regex_xyxy(VecItemPtr &inout_ptr, const RegexType &regex_, int x0, int y0, int x1, int y1) const {
            return ptr2_reg_common3(inout_ptr, std::vector<RegexType>{regex_},
                                    [x0, y0, x1, y1](const ItemType &item) {
                                        return item.x0 > x0 && item.x1 < x1 && item.y0 > y0 && item.y1 < y1;
                                    });
        }

        MAYBE_USE bool loc2_regex_x0x1(VecItemPtr &inout_ptr, const RegexType &regex_, int x0, int x1) const {
            return ptr2_reg_common3(inout_ptr, std::vector<RegexType>{regex_},
                                    [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE bool loc2_regex_y0y1(VecItemPtr &inout_ptr, const RegexType &regex_, int y0, int y1) const {
            return ptr2_reg_common3(inout_ptr, std::vector<RegexType>{regex_},
                                    [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE bool loc2_regex_x0y0(VecItemPtr &inout_ptr, const RegexType &regex_, int x0, int y0) const {
            return ptr2_reg_common3(inout_ptr, std::vector<RegexType>{regex_},
                                    [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE bool loc2_regex_x1y1(VecItemPtr &inout_ptr, const RegexType &regex_, int x1, int y1) const {
            return ptr2_reg_common3(inout_ptr, std::vector<RegexType>{regex_},
                                    [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }

        MAYBE_USE bool loc2_regex_x0x1(VecItemPtr &inout_ptr, const VecRegex &regex_, int x0, int x1) const {
            return ptr2_reg_common3(inout_ptr, regex_,
                                    [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE bool loc2_regex_y0y1(VecItemPtr &inout_ptr, const VecRegex &regex_, int y0, int y1) const {
            return ptr2_reg_common3(inout_ptr, regex_,
                                    [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE bool loc2_regex_x0y0(VecItemPtr &inout_ptr, const VecRegex &regex_, int x0, int y0) const {
            return ptr2_reg_common3(inout_ptr, regex_,
                                    [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE bool loc2_regex_x1y1(VecItemPtr &inout_ptr, const VecRegex &regex_, int x1, int y1) const {
            return ptr2_reg_common3(inout_ptr, regex_,
                                    [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }

        ///////////////////////////////////////////////////////////////
        //////                   Cross class function
        ///////////////////////////////////////////////////////////////
        MAYBE_USE BaseType DF_loc_regex(const RegexType &regex_) const {
            return DF_reg_impl(std::vector<RegexType>{regex_});
        }

        MAYBE_USE BaseType DF_loc_regex(const VecRegex &regex_) const {
            return DF_reg_impl(regex_);
        }

        MAYBE_USE BaseType DF_loc_regex_grate_strlen(const RegexType &regex_, int len) const {
            return DF_reg_common3(std::vector<RegexType>{regex_}, len, std::greater<>());
        }

        MAYBE_USE BaseType DF_loc_regex_equal_strlen(const RegexType &regex_, int len) const {
            return DF_reg_common3(std::vector<RegexType>{regex_}, len, std::equal_to<>());
        }

        MAYBE_USE BaseType DF_loc_regex_less_strlen(const RegexType &regex_, int len) const {
            return DF_reg_common3(std::vector<RegexType>{regex_}, len, std::less<>());
        }

        MAYBE_USE BaseType DF_loc_regex_grate_strlen(const VecRegex &regex_, int len) const {
            return DF_reg_common3(regex_, len, std::greater<>());
        }

        MAYBE_USE BaseType DF_loc_regex_equal_strlen(const VecRegex &regex_, int len) const {
            return DF_reg_common3(regex_, len, std::equal_to<>());
        }

        MAYBE_USE BaseType DF_loc_regex_less_strlen(const VecRegex &regex_, int len) const {
            return DF_reg_common3(regex_, len, std::less<>());
        }


        MAYBE_USE BaseType DF_loc_regex_xyxy(const RegexType &regex_, int x0, int y0, int x1, int y1) const {
            return DF_reg_common2(std::vector<RegexType>{regex_},
                                  [x0, y0, x1, y1](const ItemType &item) {
                                      return item.x0 > x0 && item.x1 < x1 && item.y0 > y0 && item.y1 < y1;
                                  });
        }

        MAYBE_USE BaseType DF_loc_regex_x0x1(const RegexType &regex_, int x0, int x1) const {
            return DF_reg_common2(std::vector<RegexType>{regex_},
                                  [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE BaseType DF_loc_regex_y0y1(const RegexType &regex_, int y0, int y1) const {
            return DF_reg_common2(std::vector<RegexType>{regex_},
                                  [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE BaseType DF_loc_regex_x0y0(const RegexType &regex_, int x0, int y0) const {
            return DF_reg_common2(std::vector<RegexType>{regex_},
                                  [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE BaseType DF_loc_regex_x1y1(const RegexType &regex_, int x1, int y1) const {
            return DF_reg_common2(std::vector<RegexType>{regex_},
                                  [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }

        MAYBE_USE BaseType DF_loc_regex_x0x1(const VecRegex &regex_, int x0, int x1) const {
            return DF_reg_common2(regex_, [x0, x1](const ItemType &item) { return item.x0 > x0 && item.x1 < x1; });
        }

        MAYBE_USE BaseType DF_loc_regex_y0y1(const VecRegex &regex_, int y0, int y1) const {
            return DF_reg_common2(regex_, [y0, y1](const ItemType &item) { return item.y0 > y0 && item.y1 < y1; });
        }

        MAYBE_USE BaseType DF_loc_regex_x0y0(const VecRegex &regex_, int x0, int y0) const {
            return DF_reg_common2(regex_, [x0, y0](const ItemType &item) { return item.x0 > x0 && item.y0 > y0; });
        }

        MAYBE_USE BaseType DF_loc_regex_x1y1(const VecRegex &regex_, int x1, int y1) const {
            return DF_reg_common2(regex_, [x1, y1](const ItemType &item) { return item.x1 < x1 && item.y1 < y1; });
        }


    private:
        //////////////////////////////////////////////////////////////////////
        template<typename MapType>
        void multi_replace_df_text_impl(const MapType &replacements);

        bool ptr_reg_impl(VecItemPtr &out_ptr, const VecRegex &regex_) const;

        bool ptr_reg_common4(
                VecItemPtr &out_ptr,
                const VecRegex &regex_, int len,
                const std::function<bool(size_t, size_t)> &compare) const;


        bool ptr_reg_common3(
                VecItemPtr &out_indices,
                const VecRegex &regex_,
                const std::function<bool(const ItemType &)> &condition) const;

        bool ptr2_reg_impl(VecItemPtr &out_ptr, const VecRegex &regex_) const;


        bool ptr2_reg_common4(
                VecItemPtr &out_ptr,
                const VecRegex &regex_, int len,
                const std::function<bool(size_t, size_t)> &compare) const;


        bool ptr2_reg_common3(
                VecItemPtr &out_ptr,
                const VecRegex &regex_,
                const std::function<bool(const ItemType &)> &condition) const;

        //////////////////////////////////////////////////////////////////////
        BaseType DF_reg_impl(const VecRegex &regex_) const;

        BaseType DF_reg_common3(
                const VecRegex &regex_, int len,
                const std::function<bool(size_t, size_t)> &compare) const;

        BaseType DF_reg_common2(
                const VecRegex &regex_,
                const std::function<bool(const ItemType &)> &condition) const;
    };


} // NDFrame


#include "ZColView.inl"
#endif //ZDATAFRAME_COLUMNVIEW_H
